﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using Microsoft.SharePoint;

namespace SPSiteColumnUsage
{
    /// <summary>
    /// Class with site column usage information.
    /// </summary>
    public class SPSiteColumnUsage
    {
        /// <summary>
        /// Get usage information about a site column.
        /// </summary>
        /// <param name="field">The field.</param>
        /// <returns>Returns a list of content types and listUrl where the specified site column is in use.</returns>
        public static IList<SPSiteColumnUsage> GetUsages(SPField field)
        {
            List<SPSiteColumnUsage> list = new List<SPSiteColumnUsage>();

            if(field != null && field.UsedInWebContentTypes)
            {
                // Use reflection to get the fields collection for the specified field
                SPFieldCollection fieldCollection = field.GetType().GetProperty("Fields", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(field, null) as SPFieldCollection;
                if(fieldCollection != null)
                {
                    // Get the web context from the collection
                    SPWeb web = fieldCollection.Web;

                    // First collect all contenttypes to an array, so we can use Linq
                    var contentTypes = web.ContentTypes.CopyToArray<SPContentType>();

                    // Filter contenttypes where field is used
                    var contentTypesWithField = (from contentType in contentTypes
                                                 where contentType.Fields.ContainsField(field.InternalName)
                                                 select contentType).ToArray();

                    // Create usages for fields in contenttypes
                    foreach(SPContentType contentType in contentTypesWithField)
                    {
                        SPSiteColumnUsage siteColumnUsage = new SPSiteColumnUsage(field.Id, field.Scope, contentType.Id);
                        list.Add(siteColumnUsage);
                    }

                    // Get unique lists-urls where contentypes are beeing used
                    var listUrlsContentTypeUsage = (from contentType in contentTypesWithField
                                                    let contentTypeUsages = SPContentTypeUsage.GetUsages(contentType)
                                                    from contentTypeUsage in contentTypeUsages
                                                    where contentTypeUsage.IsUrlToList
                                                    select contentTypeUsage.Url).Distinct().ToArray();

                    // Create usages for fields in list
                    foreach (string listUrl in listUrlsContentTypeUsage)
                    {
                        SPSiteColumnUsage siteColumnUsage = new SPSiteColumnUsage(field.Id, field.Scope, listUrl);
                        list.Add(siteColumnUsage);
                    }
                }
            }

            return list;
        }

        private readonly Guid _Id;
        private readonly string _Scope;
        private readonly SPContentTypeId _ContentTypeId;
        private readonly string _Url;

        /// <summary>
        /// Initializes a new instance of the <see cref="SPSiteColumnUsage"/> class.
        /// </summary>
        /// <param name="id">The id.</param>
        /// <param name="scope">The scope.</param>
        /// <param name="contentTypeId">The content type id.</param>
        private SPSiteColumnUsage(Guid id, string scope, SPContentTypeId contentTypeId)
        {
            _Id = id;
            _Scope = scope;
            _ContentTypeId = contentTypeId;
            _Url = null;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="SPSiteColumnUsage"/> class.
        /// </summary>
        /// <param name="id">The id.</param>
        /// <param name="scope">The scope.</param>
        /// <param name="url">The URL.</param>
        private SPSiteColumnUsage(Guid id, string scope, string url)
        {
            _Id = id;
            _Scope = scope;
            _ContentTypeId = SPContentTypeId.Empty;
            _Url = url;
        }

        /// <summary>
        /// Gets the id.
        /// </summary>
        /// <value>The id of the site column.</value>
        public Guid Id
        {
            [DebuggerStepThrough]
            get { return _Id; }
        }

        /// <summary>
        /// Gets the scope.
        /// </summary>
        /// <value>The scope of the site column.</value>
        public string Scope
        {
            [DebuggerStepThrough]
            get { return _Scope; }
        }

        /// <summary>
        /// Gets the content type id or returns SPContentTypeId.Empty when <see cref="IsUrlToList"/> equals <c>true</c>.
        /// </summary>
        /// <value>The content type id.</value>
        public SPContentTypeId ContentTypeId
        {
            [DebuggerStepThrough]
            get { return _ContentTypeId; }
        }

        /// <summary>
        /// Gets the URL.
        /// </summary>
        /// <value>The server relative URL to a list.</value>
        public string Url
        {
            [DebuggerStepThrough]
            get { return _Url; }
        }

        /// <summary>
        /// Gets a value indicating whether this instance is a URL to a list.
        /// </summary>
        /// <value>
        /// 	<c>true</c> if this instance is a URL to a list; otherwise, <c>false</c>.
        /// </value>
        public bool IsUrlToList
        {
            [DebuggerStepThrough]
            get { return _ContentTypeId.Equals(SPContentTypeId.Empty) && !string.IsNullOrEmpty(_Url); }
        }
    }
}
